package com.bytedance.retrofit2;

import com.bytedance.retrofit2.client.ClientCall;
import com.bytedance.retrofit2.client.Header;
import com.bytedance.retrofit2.client.Request;
import com.bytedance.retrofit2.client.SsResponse;
import com.bytedance.retrofit2.mime.TypedInput;
import com.bytedance.retrofit2.mime.TypedOutput;

import java.io.IOException;
import java.util.Collections;
import java.util.List;

public final class SsCall<T> implements Call<T> {
    protected final ServiceMethod<T> serviceMethod;
    protected final Object[] args;
    protected ClientCall rawCall;
    protected volatile boolean canceled;
    protected Throwable creationFailure;
    protected boolean executed;

    SsCall(ServiceMethod serviceMethod, Object[] args) {
        this.serviceMethod = serviceMethod;
        this.args = args;
    }

    public static ServiceMethod getServiceMethod(SsCall ssCall) {
        return ssCall.serviceMethod;
    }

    final ResponseWrapper<T> parseResponse(SsResponse ssResponse) throws IOException {
        if (ssResponse == null) {
            throw new IOException("SsResponse is null");
        }
        TypedInput rawBody = ssResponse.body;
        int statusCode = ssResponse.status;
        if (statusCode < 200 || statusCode >= 300) {
            if (rawBody == null) {
                throw new NullPointerException("body == null");
            }

            if (ssResponse == null) {
                throw new NullPointerException("rawResponse == null");
            }

            if (ssResponse.success()) {
                throw new IllegalArgumentException("rawResponse should not be successful response");
            }
            return new ResponseWrapper(ssResponse, null);
        } else {
            if (statusCode != 204 && statusCode != 205) {
                try {
                    T body = this.serviceMethod.responseConverter.convert(rawBody);
                    return ResponseWrapper.createResponseWrapper(body, ssResponse);
                } catch (RuntimeException e) {
                    throw e;
                }
            }
            return ResponseWrapper.createResponseWrapper(null, ssResponse);
        }
    }

    protected void interceptResponse(ClientCall ssCall, ResponseWrapper responseWrapper) throws Throwable {
        ResponseInterceptor responseInterceptor = this.serviceMethod.responseInterceptor;
        if (responseInterceptor != null) {
            responseInterceptor.intercept(ssCall.request(), responseWrapper);
        }
    }

    public synchronized final ResponseWrapper<T> execute() throws IOException {
        ClientCall call;
        synchronized (this) {
            if (this.executed) {
                throw new IllegalStateException("Already executed.");
            }
            this.executed = true;
            if (this.creationFailure != null) {
                if ((this.creationFailure instanceof IOException)) {
                    throw (IOException) this.creationFailure;
                } else {
                    throw (RuntimeException) this.creationFailure;
                }
            }
            call = this.rawCall;
            if (call == null) {
                try {
                    call = this.rawCall = this.createRawCall();
                } catch (RuntimeException e) {
                    this.creationFailure = e;
                    throw (RuntimeException) this.creationFailure;
                } catch (IOException e) {
                    this.creationFailure = e;
                    throw (IOException) this.creationFailure;
                }
            }
        }

        if (this.canceled) {
            call.cancel();
        }
        ResponseWrapper responseWrapper = this.parseResponse(call.execute());
        try {
            this.interceptResponse(call, responseWrapper);
        } catch (Throwable throwable) {
            if (!(throwable instanceof IOException)) {
                return responseWrapper;
            }
            throw (IOException) throwable;
        }
        return responseWrapper;
    }

    public final void enqueue(Callback callback) {
        if (callback == null) {
            throw new NullPointerException("callback == null");
        }

        if (this.executed) {
            throw new IllegalStateException("Already executed.");
        }

        this.serviceMethod.httpExecutor.execute(new CallbackRunnable(this, callback));
    }

    public final void cancel() {
        this.canceled = true;
        synchronized (this) {
            ClientCall rawCall = this.rawCall;
            if (rawCall != null) {
                rawCall.cancel();
            }
        }

    }

    public final boolean isCanceled() {
        return this.canceled;
    }

    public final Call<T> clone() throws CloneNotSupportedException {
        return this.createSsHttpCall();
    }

    public final Call d() {
        return this.createSsHttpCall();
    }

    protected ClientCall createRawCall() throws IOException {
        TypedOutput body2;
        ServiceMethod serviceMethod = this.serviceMethod;
        RequestBuilder requestBuilder = new RequestBuilder(serviceMethod.httpMethod,
                serviceMethod.baseUrl, serviceMethod.relativeUrl, serviceMethod.headers,
                serviceMethod.contentType, serviceMethod.requestInterceptor, serviceMethod.priorityLevel,
                serviceMethod.isStreaming, serviceMethod.j, serviceMethod.k, serviceMethod.l,
                serviceMethod.isFormEncoded, serviceMethod.isMultipart);
        ParameterHandler[] handlers = serviceMethod.parameterHandlers;
        int argumentCount = args != null ? args.length : 0;
        if (argumentCount != handlers.length) {
            throw new IllegalArgumentException("Argument count (" + argumentCount + ") doesn\'t match expected count (" + handlers.length + ")");
        }

        for (int i = 0; i < argumentCount; ++i) {
            handlers[i].apply(requestBuilder, args[i]);
        }

        if (requestBuilder.multipartBody != null && requestBuilder.multipartBody.mimeParts.size() == 0) {
            throw new IllegalStateException("Multipart requests must contain at least one part.");
        }

        StringBuilder stringBuilder = new StringBuilder(requestBuilder.httpUrl.url);
        if (requestBuilder.httpUrl.url.endsWith("/")) {
            stringBuilder.deleteCharAt(stringBuilder.length() - 1);
        }

        stringBuilder.append(requestBuilder.relativeUrl);
        StringBuilder sb = requestBuilder.e;
        if (sb != null) {
            if (63 == sb.charAt(0)
                    && requestBuilder.relativeUrl != null
                    && requestBuilder.relativeUrl.indexOf(63) != -1) {
                sb.setCharAt(0, '&');
            }
            stringBuilder.append(sb);
        }

        requestBuilder.url = stringBuilder.toString();
        if (requestBuilder.requestInterceptor != null) {
            requestBuilder.requestInterceptor.intercept(requestBuilder);
        }

        TypedOutput body = requestBuilder.body;
        List<Header> headers = requestBuilder.headers;
        if (requestBuilder.contentType == null) {
            body2 = body;
        } else if (body != null) {
            body2 = new RequestBuilder.MimeOverridingTypedOutput(body, requestBuilder.contentType);
        } else {
            Header header = new Header("Content-Type", requestBuilder.contentType);
            if (headers == null) {
                headers = Collections.singletonList(header);
                body2 = body;
            } else {
                headers.add(header);
                body2 = body;
            }
        }

        return this.serviceMethod.clientProvider.get().execute(
                new Request(
                        requestBuilder.httpMethod,
                        requestBuilder.url,
                        headers,
                        body2,
                        requestBuilder.isStreaming,
                        requestBuilder.m,
                        requestBuilder.o)
        );
    }

    private SsCall<T> createSsHttpCall() {
        return new SsCall(this.serviceMethod, this.args);
    }
}

